package biolabs.space;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents a position in a two-dimensional grid.
 * That is a vector from
 * {@latex.inline $\\mathbb{Z} \\times \\mathbb{Z}$},
 * where {@latex.inline $\\mathbb{Z}$} is the set of integers.
 *
 * Instances are immutable.
 */
public class Discrete2DPosition
		extends DiscretePosition<Discrete2DPosition, Continuous2DPosition>
		implements Comparable<Discrete2DPosition> {

	public static final Discrete2DPosition ZERO = new Discrete2DPosition(0, 0);
	public static final Discrete2DPosition ONE = new Discrete2DPosition(1, 1);

	final int x;
	final int y;

	public Discrete2DPosition(int x, int y){
		this.x = x;
		this.y = y;
	}

	public final int getX(){
		return x;
	}

	public final int getY(){
		return y;
	}

	@Override
	public boolean isEqualTo(Discrete2DPosition p){
		return x==p.x && y==p.y;
	}

	@Override
	public int compareTo(Discrete2DPosition p) {
		if(x < p.x)
			return -1;
		if(x > p.x)
			return 1;
		if(y < p.y)
			return -1;
		if(y > p.y)
			return 1;
		return 0;
	}

	@Override
	@Deprecated
	public boolean equals(Object o){
		if(!(o instanceof Discrete2DPosition))
			return false;
		return isEqualTo((Discrete2DPosition)o);
	}

	@Override
	public int hashCode(){
		return x*31 + y;
	}

	@Override
	public String toString(){
		return "("+x+','+y+')';
	}

	@Override
	public Discrete2DPosition one() {
		return ONE;
	}

	@Override
	public Discrete2DPosition zero() {
		return ZERO;
	}

	@Override
	public Continuous2DPosition continuous() {
		return new Continuous2DPosition(x, y);
	}

	@Override
	public List<Discrete2DPosition> getNeighbors() {
		List<Discrete2DPosition> n = new ArrayList<Discrete2DPosition>(4);
		n.add(new Discrete2DPosition(x-1, y));
		n.add(new Discrete2DPosition(x+1, y));
		n.add(new Discrete2DPosition(x, y-1));
		n.add(new Discrete2DPosition(x, y+1));
		return n;
	}

	@Override
	public Integer innerProduct(Discrete2DPosition a) {
		return x*a.x + y*a.y;
	}

	@Override
	public double length() {
		return Math.sqrt(x*x + y*y);
	}

	@Override
	public Discrete2DPosition minus(Discrete2DPosition a) {
		return new Discrete2DPosition(x-a.x, y-a.y);
	}

	@Override
	public Discrete2DPosition plus(Discrete2DPosition a) {
		return new Discrete2DPosition(x+a.x, y+a.y);
	}

	@Override
	public Discrete2DPosition times(Integer k) {
		return new Discrete2DPosition(k*x, k*y);
	}

	@Override
	public Discrete2DPosition mapMax(Discrete2DPosition a) {
		return new Discrete2DPosition(Math.max(x, a.x), Math.max(y, a.y));
	}

	@Override
	public Discrete2DPosition mapMin(Discrete2DPosition a) {
		return new Discrete2DPosition(Math.min(x, a.x), Math.min(y, a.y));
	}

	@Override
	public Integer getCoordinate(int i) {
		switch(i){
		case 0: return x;
		case 1: return y;
		default: throw new IndexOutOfBoundsException(i+" is an invalid index into a 2-dimensional vector");
		}
	}

	@Override
	public int getDimension() {
		return 2;
	}

	@Override
	public Discrete2DPosition withChangedCoordinate(int idx, Integer value)
			throws IndexOutOfBoundsException {
		switch(idx){
		case 1: return new Discrete2DPosition(x, value);
		case 0: return new Discrete2DPosition(value, y);
		default: throw new IndexOutOfBoundsException(idx+"is an invalid index into a 2-dimensional vector");
		}
	}

	@Override
	public Discrete2DPosition newInstance(int[] coords) {
		if(coords.length != 2)
			throw new IllegalArgumentException("Expected int[2], but int["+coords.length+"] supplied");
		return new Discrete2DPosition(coords[0], coords[1]);
	}

}
